package com.atguigu.tingshu.album.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.AlbumStatMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackStatMapper;
import com.atguigu.tingshu.album.service.TrackInfoService;
import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.album.TrackStat;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.album.*;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;


@Slf4j
@Service
public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo> implements TrackInfoService {

    private final AlbumInfoMapper albumInfoMapper;
    private final AlbumStatMapper albumStatMapper;
    private final TrackStatMapper trackStatMapper;
    private final VodService vodService;
    private final UserFeignClient userFeignClient;
    private final RedisTemplate<Object, Object> redisTemplate;

    public TrackInfoServiceImpl(AlbumInfoMapper albumInfoMapper, AlbumStatMapper albumStatMapper, TrackStatMapper trackStatMapper, VodService vodService, UserFeignClient userFeignClient, RedisTemplate<Object, Object> redisTemplate) {
        this.albumInfoMapper = albumInfoMapper;
        this.albumStatMapper = albumStatMapper;
        this.trackStatMapper = trackStatMapper;
        this.vodService = vodService;
        this.userFeignClient = userFeignClient;
        this.redisTemplate = redisTemplate;
    }

    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void saveTrackInfo(Long userId, TrackInfoVo trackInfoVo) {
        // 保存声音信息（track_info）
        TrackInfo trackInfo = BeanUtil.copyProperties(trackInfoVo, TrackInfo.class);
        trackInfo.setUserId(userId);
        trackInfo.setSource("1");
        trackInfo.setStatus(SystemConstant.TRACK_STATUS_PASS);
        // 计算声音在专辑中的排序值，从0开始依次递增，值越小排序越前
        AlbumInfo albumInfo = this.albumInfoMapper.selectById(trackInfoVo.getAlbumId());
        trackInfo.setOrderNum(albumInfo.getIncludeTrackCount() + 1);
        // 根据文件唯一标识远程调用腾讯点播平台接口获取音频文件信息（音频时长、大小、音频类型）
        TrackMediaInfoVo trackMediaInfoVo = this.vodService.getTrackMediaInfo(trackInfoVo.getMediaFileId());
        if (trackMediaInfoVo != null) {
            trackInfo.setMediaDuration(BigDecimal.valueOf(trackMediaInfoVo.getDuration()));
            trackInfo.setMediaSize(trackMediaInfoVo.getSize());
            trackInfo.setMediaType(trackMediaInfoVo.getType());
        }
        this.baseMapper.insert(trackInfo);

        // 保存声音统计信息（track_stat）统计类型：0701-播放量 0702-收藏量 0703-点赞量 0704-评论数
        Long trackId = trackInfo.getId();
        this.saveTrackStat(trackId, SystemConstant.TRACK_STAT_PLAY, 0);
        this.saveTrackStat(trackId, SystemConstant.TRACK_STAT_COLLECT, 0);
        this.saveTrackStat(trackId, SystemConstant.TRACK_STAT_PRAISE, 0);
        this.saveTrackStat(trackId, SystemConstant.TRACK_STAT_COMMENT, 0);

        // 更新专辑信息（album_info）
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() + 1);
        this.albumInfoMapper.updateById(albumInfo);

    }

    @Override
    public void saveTrackStat(Long trackId, String statType, int statNum) {
        TrackStat trackStat = new TrackStat();
        trackStat.setTrackId(trackId);
        trackStat.setStatType(statType);
        trackStat.setStatNum(statNum);
        this.trackStatMapper.insert(trackStat);
    }

    @Override
    public IPage<TrackListVo> getUserTrackPage(Page<TrackListVo> page, TrackInfoQuery trackInfoQuery) {
        return this.baseMapper.selectUserTrackPage(page, trackInfoQuery);
    }

    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void updateTrackInfoById(Long id, TrackInfoVo trackInfoVo) {
        // 根据声音ID查询声音信息，用于判断声音是否变更
        TrackInfo trackInfo = this.baseMapper.selectById(id);
        String mediaFileIdBefore = trackInfo.getMediaFileId();
        String mediaFileIdAfter = trackInfoVo.getMediaFileId();
        BeanUtil.copyProperties(trackInfoVo, trackInfo);

        // 如果声音变更
        if (!mediaFileIdBefore.equals(mediaFileIdAfter)) {
            // 获取最新声音相关音频信息（时长、大小、类型）为数据库实体对象赋值
            TrackMediaInfoVo trackMediaInfo = this.vodService.getTrackMediaInfo(mediaFileIdAfter);
            if (trackMediaInfo != null) {
                trackInfo.setMediaDuration(BigDecimal.valueOf(trackMediaInfo.getDuration()));
                trackInfo.setMediaSize(trackMediaInfo.getSize());
                trackInfo.setMediaType(trackMediaInfo.getType());
                // 将变更前声音文件从云点播平台删除
                this.vodService.deleteTrackMedia(mediaFileIdBefore);
            }
        }

        // 更新声音信息
        this.baseMapper.updateById(trackInfo);
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void removeTrackInfoById(Long id) {
        // 根据要删除声音ID查询声音对象，用于获取当前声音序号更新其他声音序号（orderNum）
        TrackInfo trackInfo = this.baseMapper.selectById(id);
        // 删除声音信息（track_info）
        this.baseMapper.deleteById(id);

        // 将比删除声音序号大的声音序号进行递减
        this.baseMapper.updateTrackOrderNum(trackInfo.getOrderNum(), trackInfo.getAlbumId());

        // 删除声音统计信息（track_stat）
        this.trackStatMapper.delete(new LambdaQueryWrapper<TrackStat>()
                .eq(TrackStat::getTrackId, id));

        // 更新专辑信息（album_info），专辑内声音数量减1
        AlbumInfo albumInfo = this.albumInfoMapper.selectById(trackInfo.getAlbumId());
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() - 1);
        this.albumInfoMapper.updateById(albumInfo);

        // 删除云点播平台音频文件
        this.vodService.deleteTrackMedia(trackInfo.getMediaFileId());

    }

    @Override
    public Page<AlbumTrackListVo> getAlbumTrackPage(Page<AlbumTrackListVo> trackPage, Long albumId, Long userId) {
        // 根据专辑id查询专辑信息，获取专辑付费类型: 0101-免费、0102-vip免费、0103-付费
        AlbumInfo albumInfo = this.albumInfoMapper.selectById(albumId);
        // 校验专辑是否存在
        Assert.notNull(albumInfo, "专辑：{}不存在", albumId);
        String payType = albumInfo.getPayType();
        // 如果是免费专辑，直接返回结果，不显示付费标识
        if (SystemConstant.ALBUM_PAY_TYPE_FREE.equals(payType)) {
            return trackPage;
        }

        // 根据专辑id分页获取声音信息列表，isShowPaidMark默认为 false
        trackPage = this.baseMapper.selectAlbumTrackPage(trackPage, albumId);
        // 过滤掉免费试听的声音，获取剩余的付费声音
        List<AlbumTrackListVo> needCheckList = trackPage.getRecords().stream()
                .filter(trackVo -> trackVo.getOrderNum() > albumInfo.getTracksForFree())
                .toList();


        // 用户未登录的情况
        if (userId == null) {
            // 对于 VIP 免费或付费专辑，超过免费试听范围的声音需标记为付费
            if (SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(payType) || SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(payType)) {
                needCheckList.forEach(trackVo -> trackVo.setIsShowPaidMark(true));
            }
            return trackPage;
        }

        // 用户已登录的情况
        // 如果专辑是当前用户创建的，直接返回结果，不显示付费标识
        if (albumInfo.getUserId().equals(userId)) {
            return trackPage;
        }
        // 获取用户信息
        Result<UserInfoVo> userInfoVoResult = this.userFeignClient.getUserInfoVoByUserId(userId);
        if (ResultCodeEnum.FAIL.getCode().equals(userInfoVoResult.getCode())) {
            throw new GuiguException(ResultCodeEnum.REMOTE_ERROR);
        }
        UserInfoVo userInfoVo = userInfoVoResult.getData();
        Assert.notNull(userInfoVo, "用户：{}不存在", userId);
        boolean isVip = userInfoVo.getIsVip().equals(1)
                && userInfoVo.getVipExpireTime() != null
                && userInfoVo.getVipExpireTime().after(new Date());
        // 如果专辑付费类型为 vip免费且用户是vip会员，直接返回，不显示付费标识
        if (SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(payType) && isVip) {
            return trackPage;
        }

        // 专辑需要付费的情况，获取 需要检查付费情况的声音 ID 列表
        List<Long> checkTrackIdList = needCheckList.stream()
                .map(AlbumTrackListVo::getTrackId)
                .toList();
        // 远程调用获取用户的声音付费情况
        Result<Map<Long, Integer>> checkResult = this.userFeignClient.userIsPaidTrack(userId, albumId, checkTrackIdList);
        if (ResultCodeEnum.FAIL.getCode().equals(checkResult.getCode())) {
            throw new GuiguException(ResultCodeEnum.REMOTE_ERROR);
        }
        Map<Long, Integer> userIsPaidTrack = checkResult.getData();
        // 设置付费标识：未付费的声音标记为 true
        needCheckList.forEach(trackVo -> {
            Integer payStatus = userIsPaidTrack.get(trackVo.getTrackId());
            // 如果用户没有付费，则付费标识设置为 true
            if (payStatus != null && payStatus.equals(0)) {
                trackVo.setIsShowPaidMark(true);
            }
        });

        return trackPage;
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void updateTrackStat(TrackStatMqVo trackStatMqVo) {
        Long albumId = trackStatMqVo.getAlbumId();
        String statType = trackStatMqVo.getStatType();
        Integer count = trackStatMqVo.getCount();
        String businessNo = trackStatMqVo.getBusinessNo();
        String key = RedisConstant.BUSINESS_PREFIX + businessNo;
        try {
            // 使用Redis的分布式锁做幂等性处理

            Boolean lock = this.redisTemplate.opsForValue()
                    .setIfAbsent(key, businessNo, RedisConstant.ALBUM_LOCK_EXPIRE_PX2, TimeUnit.MINUTES);
            if (Boolean.FALSE.equals(lock)) {
                return;
            }

            // 更新声音统计信息
            this.trackStatMapper.updateTrackStat(trackStatMqVo);

            // 更新专辑统计信息
            if (SystemConstant.TRACK_STAT_PLAY.equals(statType)) {
                this.albumStatMapper.updateAlbumStat(albumId, SystemConstant.ALBUM_STAT_PLAY, count);
            }
            if (SystemConstant.TRACK_STAT_COMMENT.equals(statType)) {
                this.albumStatMapper.updateAlbumStat(albumId, SystemConstant.ALBUM_STAT_COMMENT, count);
            }
        } catch (Exception e) {
            log.error("[专辑服务] 更新声音统计信息失败：{}", e.getMessage(), e);
            // 采用lua脚本释放锁
            String script = """
                    if redis.call('get', KEYS[1]) == ARGV[1] then
                        return redis.call('del', KEYS[1])
                    else
                        return 0
                    end
                    """;
            this.redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), List.of(key), businessNo);
            throw new GuiguException(ResultCodeEnum.SERVICE_ERROR);
        }

    }

    @Override
    public TrackStatVo getTrackStatVoByTrackId(Long trackId) {
        return this.trackStatMapper.selectTrackStatVoByTrackId(trackId);
    }

    @Override
    public List<Map<String, Object>> getUserTrackPaidList(Long trackId, Long userId) {
        List<TrackInfo> needBuyTrackList = this.getNeedBuyTrackList(trackId);
        TrackInfo trackInfo = this.baseMapper.selectById(trackId);
        Long albumId = trackInfo.getAlbumId();

        // 封装返回结果
        List<Map<String, Object>> userTrackPaidList = new ArrayList<>();
        // 如果 没有需要购买的声音，则直接返回
        if (CollectionUtil.isEmpty(needBuyTrackList)) {
            return userTrackPaidList;
        }
        // 查询专辑信息获取单集声音价格
        AlbumInfo albumInfo = this.albumInfoMapper.selectById(albumId);
        BigDecimal price = albumInfo.getPrice();
        // 封装本集购买结果
        userTrackPaidList.add(Map.of(
                "price", price,
                "name", "本集",
                "trackCount", 1
        ));

        // 根据 needBuyTrackList 的 size 封装剩余购买结果（后10、20、30...集数）
        int count = needBuyTrackList.size();

        for (int i = 10; i <= 50 && count > 1; i += 10) {
            if (i < 50 && i < count) {
                userTrackPaidList.add(Map.of(
                        "price", price.multiply(BigDecimal.valueOf(i)),
                        "name", "后" + i + "集",
                        "trackCount", i
                ));
            } else {
                userTrackPaidList.add(Map.of(
                        "price", price.multiply(BigDecimal.valueOf(count)),
                        "name", "后" + count + "集",
                        "trackCount", count
                ));
                break;
            }

        }

        return userTrackPaidList;
    }

    @Override
    public List<TrackInfo> getPaidTrackInfoList(Long trackId, Integer trackCount) {
        return this.getNeedBuyTrackList(trackId, trackCount);
    }


    public List<TrackInfo> getNeedBuyTrackList(Long trackId) {
        return this.getNeedBuyTrackList(trackId, null);
    }

    public List<TrackInfo> getNeedBuyTrackList(Long trackId, Integer trackCount) {
        // 查询本集声音信息
        TrackInfo trackInfo = this.baseMapper.selectById(trackId);
        Long albumId = trackInfo.getAlbumId();
        // 调用远程服务获取用户已购买声音id列表
        Result<List<Long>> userPaidTrackListResult = this.userFeignClient.findUserPaidTrackList(albumId);
        if (ResultCodeEnum.FAIL.getCode().equals(userPaidTrackListResult.getCode())) {
            throw new GuiguException(ResultCodeEnum.REMOTE_ERROR);
        }
        List<Long> userPaidTrackList = userPaidTrackListResult.getData();
        // 构建查询条件，查询用户下单付费声音列表
        LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<TrackInfo>()
                // 根据专辑id查询
                .eq(TrackInfo::getAlbumId, albumId)
                // orderNum >= 本集声音的orderNum
                .ge(TrackInfo::getOrderNum, trackInfo.getOrderNum())
                // 筛选出未购买声音
                .notIn(CollectionUtil.isNotEmpty(userPaidTrackList), TrackInfo::getId, userPaidTrackList)
                // 根据序号升序排序
                .orderByAsc(TrackInfo::getOrderNum);
        if (trackCount != null) {
            // 获取指定数量的声音(用户选择购买数量)
            queryWrapper.last(" limit " + trackCount);
        }
        List<TrackInfo> needBuyTrackList = this.baseMapper.selectList(queryWrapper);
        Assert.notEmpty(needBuyTrackList, "专辑：{} 下没有符合购买要求的声音", albumId);
        return needBuyTrackList;
    }


}
